//
//  FMDBHelper.m
//  FMDBHelper
//
//  Created by runo on 16/5/25.
//  Copyright © 2016年 com.runo. All rights reserved.
//

#import "CQDBHelper.h"
#import "FMDB.h"

#import <objc/runtime.h>
#define kTableName NSStringFromClass([[self class] cqModelClass])

@interface CQDBHelper ()

@property(nonatomic,strong) FMDatabase *fmdatabase;//!<数据库操作

@end

@implementation CQDBHelper

static CQDBHelper *instance;//数据库操作实例
/**屏蔽单例*/
- (id)copyWithZone:(NSZone *)zone
{
    return instance;
}

+(id)cqShareDMOperation{
    
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        
        instance = [[self alloc]init];
        
        instance.fmdatabase = [[FMDatabase alloc]initWithPath:[self cqDBPath]];
    });
    return instance;
}

+(Class)cqModelClass{
    
    [NSException raise:@"FMDBHelper" format:@"sonclass must implementation 'modelClass' method"];
    return nil;
}

-(BOOL)cqDeleteAllModel{
    BOOL ret = false;
    [self.fmdatabase open];
    
    NSString *sql = [NSString stringWithFormat:@"delete from %@",kTableName];
    ret = [self.fmdatabase executeUpdate:sql];
    [self.fmdatabase close];
    return ret;
}

/**好像不能判断某个属性为空*/
-(BOOL)cqDeleteModelWhere:(NSDictionary *)dic{
    
    BOOL ret = false;
    [self.fmdatabase open];
    NSMutableString *mstr = [NSMutableString stringWithFormat:@"delete from %@ ",kTableName];
    NSArray *keys = [dic allKeys];
    NSMutableArray *values = [[NSMutableArray alloc]init];
    for (int i = 0; i < keys.count; i++) {
        
        if (i == 0) {
            [mstr appendString:@"where "];
        }
        
        if (i != keys.count-1) {
            [mstr appendFormat:@"%@ = ? and",keys[i]];
        }else{
            [mstr appendFormat:@"%@ = ?",keys[i]];
        }
        [values addObject:[dic valueForKey:keys[i]]];
    }
    NSError *error = nil;
    ret = [self.fmdatabase executeUpdate:mstr values:values error:&error];
    if (!ret && error != nil) {
        NSLog(@"%@",error);
    }
    
       // NSString *sql = [NSString stringWithFormat:@"delete from %@ where %@ = ",kTableName,key];
       // ret = [self.fmdatabase executeUpdate:sql,[dic valueForKey:key]];

    [self.fmdatabase close];
    return ret;
    
}

-(NSArray *)cqGetModelWhere:(NSDictionary *)dic{
    
    NSMutableArray *marr = [[NSMutableArray alloc]init];
    NSMutableString *msql = [NSMutableString stringWithFormat:@"select * from %@ ",kTableName];
    NSArray *keys = [dic allKeys];
    NSMutableArray *values = [[NSMutableArray alloc]init];
    for (int i = 0; i < keys.count; i++) {
        
        if (i == 0) {
            [msql appendString:@"where "];
        }
        
        if (i != keys.count-1) {
            [msql appendFormat:@"%@ = ? and",keys[i]];
        }else{
            [msql appendFormat:@"%@ = ?",keys[i]];
        }
        [values addObject:[dic valueForKey:keys[i]]];
    }
    [self.fmdatabase open];
    NSError *error = nil;
    FMResultSet *resultSet = [self.fmdatabase executeQuery:msql values:values error:&error];
    if (error != nil) {
        NSLog(@"%@",error);
        return marr;
    }
    
    while ([resultSet next]) {
        
        id model = [[[[self class] cqModelClass]alloc]init];
        NSDictionary *propertyDic = [[self class] cqGetPropertys];
        NSArray *propers = propertyDic[@"name"];
        NSArray *types = propertyDic[@"type"];
        for (int i = 0; i < propers.count; i++) {
            
            NSString *tmpType = types[i];
            NSString *tmpProper = propers[i];
            
            if ([tmpType isEqualToString:SQLType_text]) {
                
                NSString *value = [resultSet stringForColumn:tmpProper];
                [model setValue:value forKey:tmpProper];
            }else if ([tmpType isEqualToString:SQLType_blob]){
                
                NSData *data = [resultSet dataForColumn:tmpProper];
                if (data != nil){
                    id value = [NSKeyedUnarchiver unarchiveObjectWithData:data];
                    [model setValue:value forKey:tmpProper];
                }
                
            }else if ([tmpType isEqualToString:SQLType_double]
                      ||[tmpType isEqualToString:SQLType_float]){
                
                double dou = [resultSet doubleForColumn:tmpProper];
                [model setValue:@(dou) forKey:tmpProper];
            }else if ([tmpType isEqualToString:SQLType_int]){
                
                NSInteger intData = [resultSet longForColumn:tmpProper];
                [model setValue:@(intData) forKey:tmpProper];
            }else{
                NSLog(@"error type %@",tmpType);
            }
        }
        [marr addObject:model];
    }
    [self.fmdatabase close];
    return marr;
}

-(NSArray *)cqGetAllModel{
    
    NSString *sql = [NSString stringWithFormat:@"select * from %@",kTableName];
    [self.fmdatabase open];
    NSMutableArray *marr = [[NSMutableArray alloc]init];
    
    FMResultSet *resultSet = [self.fmdatabase executeQuery:sql];
    while ([resultSet next]) {
     
        id model = [[[[self class] cqModelClass]alloc]init];
        NSDictionary *propertyDic = [[self class] cqGetPropertys];
        NSArray *propers = propertyDic[@"name"];
        NSArray *types = propertyDic[@"type"];
        for (int i = 0; i < propers.count; i++) {
            
            NSString *tmpType = types[i];
            NSString *tmpProper = propers[i];
            
            if ([tmpType isEqualToString:SQLType_text]) {
                
                NSString *value = [resultSet stringForColumn:tmpProper];
                [model setValue:value forKey:tmpProper];
            }else if ([tmpType isEqualToString:SQLType_blob]){

                NSData *data = [resultSet dataForColumn:tmpProper];
                if (data != nil){
                    id value = [NSKeyedUnarchiver unarchiveObjectWithData:data];
                    [model setValue:value forKey:tmpProper];
                }
                
            }else if ([tmpType isEqualToString:SQLType_double]
                      ||[tmpType isEqualToString:SQLType_float]){
                
                double dou = [resultSet doubleForColumn:tmpProper];
                [model setValue:@(dou) forKey:tmpProper];
            }else if ([tmpType isEqualToString:SQLType_int]){

                NSInteger intData = [resultSet longForColumn:tmpProper];
                [model setValue:@(intData) forKey:tmpProper];
            }else{
                NSLog(@"error type %@",tmpType);
            }
        }
        [marr addObject:model];
    }
    [self.fmdatabase close];
    return marr;
}

/**判断属性是否为空，主键为空就会蹦*/
+(BOOL)cqPropertyIsNull:(id)model property:(NSString *)proper{
    
    id value = [model valueForKey:proper];
    
    if (value == nil || [value isKindOfClass:[NSNull class]]) {
        if ([self cqIsPrimaryKey:proper]) {
            [NSException raise:@"FMDBHelper" format:@"%@ is PrimaryKey cannot be nil",proper];
        }
        return YES;
    }
    return NO;
}


/**保存所有对象*/
-(void)cqSaveAllModel:(NSArray *)arr{
    
    if([self cqCreateTable] && (arr.count>0)){
        
        for (id model in arr) {
            
            if (![model isMemberOfClass:[[self class] cqModelClass]]) {
                NSLog(@"require class is %@ not this class %@",kTableName,NSStringFromClass([model class]));
                continue;
            }
            NSMutableString *msql = [NSMutableString stringWithFormat:@"replace into %@ (",kTableName];
            NSDictionary *propertyDic = [[self class] cqGetPropertys];
            NSArray *propers = propertyDic[@"name"];
            NSArray *types = propertyDic[@"type"];
            
            for (int i = 0; i < propers.count ; i++) {
                
                if (i != propers.count-1) {
                    if ([[self class]cqPropertyIsNull:model property:propers[i]]) {
                        continue;
                    }
                    [msql appendFormat:@"%@,",propers[i]];
                }else{
                    if ([[self class]cqPropertyIsNull:model property:propers[i]]) {
                        [msql appendString:@") values("];
                    }else{
                        [msql appendFormat:@"%@) values(",propers[i]];
                    }
                    
                }
            }
            for (int i = 0; i < propers.count; i++) {
                
                if (i != propers.count-1) {
                    if ([[self class]cqPropertyIsNull:model property:propers[i]]) {
                        continue;
                    }
                    [msql appendString:@"?,"];
                }else{
                    if ([[self class]cqPropertyIsNull:model property:propers[i]]) {
                        [msql appendString:@")"];
                    }else{
                        [msql appendString:@"?)"];
                    }
                }
            }
            //NSLog(@"%@",msql);
            //NSString *sql = @"replace into Simpal(warningID,warningClass,carType,carFramID,carNumber,carImg,carData) values (?,?,?,?,?,?,?)";
            [self.fmdatabase open];
            
            NSError *error = nil;
            NSMutableArray *values = [[NSMutableArray alloc]init];
            for (int i = 0; i < propers.count; i++) {
                
                if ([[self class]cqPropertyIsNull:model property:propers[i]]) {
                    continue;
                }
                if (![types[i] isEqualToString:SQLType_blob]) {
                    [values addObject:[model valueForKey:propers[i]]];
                }else{
                    id value = [model valueForKey:propers[i]];
                    NSData *valueData = [NSKeyedArchiver archivedDataWithRootObject:value];
                    [values addObject:valueData];
                }

            }
            BOOL ret = [self.fmdatabase executeUpdate:msql values:values error:&error];
            if (!ret) {
                NSLog(@"第 %ld 个模型保存失败",[arr indexOfObject:model]);
                if (error != nil) {
                    NSLog(@"error : %@",error);
                }
            }
            [self.fmdatabase close];
        }//end for model
        
    }
    return;
    
}


-(BOOL)cqCreateTable{
    
    if (![self cqIsTableOK:kTableName]) {
        //建表
        NSDictionary *ditc = [[self class]cqGetPropertys];
        NSString *createSQL = [self cqCreateTableSQL:ditc];
        [self.fmdatabase open];
        BOOL ret = [self.fmdatabase executeUpdate:createSQL];
        if (!ret) {
            NSLog(@"创建表失败");
        }
        [self.fmdatabase close];
        return ret;
    }
    return YES;
}

/**获得建表语句*/
-(NSString *)cqCreateTableSQL:(NSDictionary *)dic{
    
    NSMutableString *msql = [[NSMutableString alloc]init];
    [msql appendString:[NSString stringWithFormat:@"create table if not exists %@ (",kTableName]];
    NSArray *propers = dic[@"name"];
    NSArray *types = dic[@"type"];
    for (NSInteger i = 0; i < propers.count; i++) {
        //添加属性
        [msql appendFormat:@"%@ %@",propers[i],types[i]];
        //判断是否是主键
        if ([[self class]cqIsPrimaryKey:propers[i]]) {
            [msql appendFormat:@" %@",SQLPrimaryKey];
        }
        //判断是否允许为空
        if (![[self class]cqIsAllowNull:propers[i]]) {
            [msql appendFormat:@" %@",SQLNot_Null];
        }
        if (i<propers.count-1) {
            [msql appendString:@","];
        }
    }//end for
    [msql appendString:@")"];
    return msql;
}

/**
 *  获取该类的所有属性名，属性类型
 */
+ (NSDictionary *)cqGetPropertys
{
    NSMutableArray *proNames = [NSMutableArray array];
    NSMutableArray *proTypes = [NSMutableArray array];
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList([self cqModelClass], &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        //获取属性名
        NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];

        if ([self cqIsIngorProperty:propertyName]) {
            continue;
        }

        [proNames addObject:propertyName];
        //获取属性类型等参数
        NSString *propertyType = [NSString stringWithCString: property_getAttributes(property) encoding:NSUTF8StringEncoding];
        /*
         c char         C unsigned char
         i int          I unsigned int
         l long         L unsigned long
         s short        S unsigned short
         d double       D unsigned double
         f float        F unsigned float
         q long long    Q unsigned long long
         B BOOL
         @ 对象类型 //指针 对象类型 如NSString 是T@"NSString"
                                             T@"NSDate"
         
         64位下long 和long long 都是Tq
         SQLite 默认支持五种数据类型TEXT、INTEGER、REAL、BLOB、NULL
         */
        //NSLog(@"%@",propertyType);
        if ([propertyType hasPrefix:@"T@\"NSString"]) {
            [proTypes addObject:SQLType_text];
        } else if ([propertyType hasPrefix:@"Ti"]||[propertyType hasPrefix:@"TI"]
                   ||[propertyType hasPrefix:@"Ts"]||[propertyType hasPrefix:@"TS"]
                   ||[propertyType hasPrefix:@"TB"]
                   ||[propertyType hasPrefix:@"Tq"] || [propertyType hasPrefix:@"TQ"]) {
            [proTypes addObject:SQLType_int];
        }else if ([propertyType hasPrefix:@"Td"]){
            [proTypes addObject:SQLType_double];
        }else if ([propertyType hasPrefix:@"Tf"]){
            [proTypes addObject:SQLType_float];
        }
        else {
            [proTypes addObject:SQLType_blob];
        }

    }
    free(properties);
    
    return [NSDictionary dictionaryWithObjectsAndKeys:proNames,@"name",proTypes,@"type",nil];
}



//判断表是否存在
- (BOOL)cqIsTableOK:(NSString *)tableName
{
    [self.fmdatabase open];
    FMResultSet *rs = [self.fmdatabase executeQuery:@"select count(*) as 'count' from sqlite_master where type ='table' and name = ?", tableName];
    while ([rs next])
    {
        // just print out what we've got in a number of formats.
        NSInteger count = [rs intForColumn:@"count"];
        if (0 == count)
        {
            NSLog(@"%@ 表不存在",tableName);
            [self.fmdatabase close];
            return NO;
        }
        else
        {
            [self.fmdatabase close];
            return YES;
        }
    }
    NSLog(@"%@ 表不存在",tableName);
    [self.fmdatabase close];
    return NO;
}

+(NSString *)cqDBPath{
    //NSStringFromClass([[self class] modelClass])]
    NSString *dbName = [NSString stringWithFormat:@"%@.sqlite",@"dataBase"];
    NSString * dbPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0]stringByAppendingPathComponent:dbName];
    //NSLog(@"DB path is %@",dbPath);

    BOOL isDirectory;
    BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:dbPath isDirectory:&isDirectory];
    //不存在则创建文件
    if (isExist == NO || isDirectory) {
        
        BOOL result = [[NSFileManager defaultManager]createFileAtPath:dbPath contents:nil attributes:nil];
        if (!result) {
            NSLog(@"创建数据库文件失败");
        }
    }
    return dbPath;
}

//默认没有主键
+(BOOL)cqIsPrimaryKey:(NSString *)property{
    return NO;
}

//是否允许为空
+(BOOL)cqIsAllowNull:(NSString *)property{
    return YES;
}

+(BOOL)cqIsIngorProperty:(NSString *)property{
    return NO;
}

@end
